﻿using System;
using System.Collections.Generic;

using MonoTorrent.Common;
using MonoTorrent.Client;

namespace SBPweb.RouTorrent.Business
{
    public class DownloadManager
    {

        #region Private fields

        protected const string DEFAULT_PATH_DOWNLOADED = @"C:\Temp\Downloaded\";
        protected const string DEFAULT_PATH_TEMP = @"C:\Temp\Incoming\";
        protected const int DEFAULT_PORT = 6969;
        protected const bool DEFAULT_USE_UPNP = true;
        protected const int DEFAULT_UPLOADSLOTS = 10;

        protected EngineSettings mEngineSettings;
        protected TorrentSettings mTorrentSettings;
        protected List<TorrentManager> mTorrentManagers;

        protected ClientEngine mTorrentClient;

        protected Dictionary<TorrentManager, List<int>> mDictUserIdsByManager;
        protected Dictionary<int, List<TorrentManager>> mDictManagersByUserId;

        #endregion

        #region Constructor logic

        public DownloadManager()
            : this(new MonoTorrent.Client.EngineSettings(DEFAULT_PATH_DOWNLOADED, DEFAULT_PORT, DEFAULT_USE_UPNP), new MonoTorrent.Client.TorrentSettings(DEFAULT_UPLOADSLOTS))
        {
        }

        public DownloadManager(EngineSettings engineSettings, TorrentSettings torrentSettings)
        {
            if (engineSettings != null)
            {
                mEngineSettings = engineSettings;
            }
            if (torrentSettings != null)
            {
                mTorrentSettings = torrentSettings;
            }

            mTorrentManagers = new List<TorrentManager>();

            mTorrentClient = new MonoTorrent.Client.ClientEngine(mEngineSettings, mTorrentSettings);

            mDictManagersByUserId = new Dictionary<int, List<TorrentManager>>();
            mDictUserIdsByManager = new Dictionary<TorrentManager, List<int>>();
            /*
            mBTClient = new BTClient();
            mBTClient.UseUPnP = true;
            mBTClient.FriendlyMode = true;
            
            mBTTracker = new OkayPublish.BTSharp.Tracker.Tracker(mBTClient.Port);

            mDictSharesByUserId = new Dictionary<int, List<ShareInfo>>();
            mDictUserIdsByShare = new Dictionary<ShareInfo, List<int>>();
            mDictHandlersByUserId = new Dictionary<int, DownloadStateChangedHandler>();

            mBTClient.DownloadFinished += new EventHandler<OkayPublish.BTSharp.Client.DownloadFinishedEventArgs>(pBTClient_DownloadFinished);
            mBTClient.PieceFinished += new EventHandler<OkayPublish.BTSharp.Client.PieceFinishedEventArgs>(pBTClient_PieceFinished);
            */
        }

        public void Initialize()
        {
            mTorrentClient.Start();
        }

        public void Finalize()
        {
            mTorrentClient.Stop();
        }

        #endregion

        #region Public methods

        public void QueueDownload(int userId, string downloadPath)
        {
            TorrentManager manager = mTorrentClient.LoadTorrent(downloadPath);

            manager.TorrentStateChanged += new EventHandler<TorrentStateChangedEventArgs>(manager_TorrentStateChanged);
            manager.PeersFound += new EventHandler<PeersAddedEventArgs>(manager_PeersFound);
            manager.PieceHashed += new EventHandler<PieceHashedEventArgs>(manager_PieceHashed);

            AdministerDictionariesAdd(userId, manager);

            mTorrentClient.Start(manager);
        }

        #endregion

        #region Public properties

        /*
        public string PathOfDownloadedFiles
        {
            get
            {
                return DEFAULT_PATH_DOWNLOADED;
            }
            set
            {
                DEFAULT_PATH_DOWNLOADED = value;
            }
        }

        public string PathOfTemporaryFiles
        {
            get
            {
                return DEFAULT_PATH_TEMP;
            }
            set
            {
                DEFAULT_PATH_TEMP = value;
            }
        }

        public string ContentProviderId
        {
            get
            {
                return mContentProviderId;
            }
            set
            {
                mContentProviderId = value;
            }
        }
        */

        #endregion

        #region Public events

        public event DownloadStateChangedHandler OnDownloadStateChanged;

        public event PieceStateChangedHandler OnPieceStateChanged;

        public event PeerStateChangedHandler OnPeerStateChanged;

        #endregion

        #region Protected download event handlers

        protected void manager_TorrentStateChanged(object sender, TorrentStateChangedEventArgs e)
        {
            if (OnDownloadStateChanged != null)
            {
                TorrentManager manager = sender as TorrentManager;
                foreach (int userId in GetUserIdsByShareInfo(manager))
                {
                    OnDownloadStateChanged(this, new DownloadStateChangedEventArgs(userId, manager, e.OldState, e.NewState));
                }
            }
        }

        protected void manager_PieceHashed(object sender, PieceHashedEventArgs e)
        {
            if (OnPieceStateChanged != null)
            {
                TorrentManager manager = sender as TorrentManager;
                OnPieceStateChanged(this, new PieceStateChangedEventArgs(manager, new PieceHashedEventArgs(e.PieceIndex, e.HashPassed)));
            }
        }

        protected void manager_PeersFound(object sender, PeersAddedEventArgs e)
        {
            if (OnPeerStateChanged != null)
            {
                TorrentManager manager = sender as TorrentManager;
                OnPeerStateChanged(this, new PeerStateChangedEventArgs(manager, e.Count));
            }
        }

        #endregion

        #region Private helper methods

        private void AdministerDictionariesAdd(int userId, TorrentManager manager)
        {
            lock (mDictManagersByUserId)
            {
                if (!mDictManagersByUserId.ContainsKey(userId))
                {
                    mDictManagersByUserId.Add(userId, new List<TorrentManager>());
                }
                mDictManagersByUserId[userId].Add(manager);
            }

            lock (mDictUserIdsByManager)
            {
                if (!mDictUserIdsByManager.ContainsKey(manager))
                {
                    mDictUserIdsByManager.Add(manager, new List<int>());
                }
                mDictUserIdsByManager[manager].Add(userId);
            }
        }

        private void AdministerDictionariesRemove(TorrentManager manager)
        {
            List<int> userIds = null;
            lock (mDictUserIdsByManager)
            {
                if (mDictUserIdsByManager.ContainsKey(manager))
                {
                    userIds = mDictUserIdsByManager[manager];
                    mDictUserIdsByManager.Remove(manager);
                }
            }

            if (userIds != null)
            {
                lock (mDictManagersByUserId)
                {
                    foreach (int i in userIds)
                    {
                        mDictManagersByUserId.Remove(i);
                    }
                }
            }
        }

        private List<int> GetUserIdsByShareInfo(TorrentManager manager)
        {
            if (mDictUserIdsByManager.ContainsKey(manager))
            {
                return mDictUserIdsByManager[manager];
            }

            return null;
        }

        #endregion

    }
}